package main

import (
	"fmt"
	b3 "github.com/magicsea/behavior3go"
	. "github.com/magicsea/behavior3go/config"
	. "github.com/magicsea/behavior3go/core"
	. "github.com/magicsea/behavior3go/loader"
	"time"
)

type MapObject struct {
	name string
	x, y int
	hp int
	world map[string]*MapObject
}

type SearchTargetList struct {
	Action
	r int
	targetsKey string
}

func (this *SearchTargetList) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.r = setting.GetPropertyAsInt("range")
	this.targetsKey = setting.GetPropertyAsString("targetsKey")
	//fmt.Println("LogTest Initialize:", time.Now().String())
}

func (this *SearchTargetList) OnTick(tick *Tick) b3.Status {
	list :=  make([]*MapObject, 0, 5)
	self := tick.GetTarget().(*MapObject)
	for _, p := range self.world {
		if p.name != self.name && p.hp > 0{
			list = append(list, p)
		}
	}

	fmt.Println("SearchTargetList ", list)
	tick.Blackboard.SetTree(this.targetsKey,list, tick.GetTree().GetID())
	return b3.SUCCESS
}

type SelectTarget struct {
	Action
	rule int
	targetsKey string
	targetKey string
}

func (this *SelectTarget) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.rule = setting.GetPropertyAsInt("rule")
	this.targetsKey = setting.GetPropertyAsString("targetsKey")
	this.targetKey = setting.GetPropertyAsString("targetKey")
}

func (this *SelectTarget) OnTick(tick *Tick) b3.Status {
	targets, ok := tick.Blackboard.Get(this.targetsKey, tick.GetTree().GetID(), "").([]*MapObject)
	if !ok {
		fmt.Println("SelectTarget error")
		return b3.FAILURE
	}

	if len(targets) == 0 {
		fmt.Println("SelectTarget empty")
		return b3.FAILURE
	}

	var target *MapObject
	switch this.rule {
	case 1:
		target = targets[0]
		targets = targets[1:]
	case 2:
		lastIndex := len(targets) - 1
		target = targets[lastIndex]
		targets = targets[:lastIndex]
	}

	fmt.Println("SelectTarget ", target.name)
	tick.Blackboard.SetTree(this.targetsKey, targets, tick.GetTree().GetID())
	tick.Blackboard.SetTree(this.targetKey, target, tick.GetTree().GetID())
	return b3.SUCCESS
}

type SetBoardSelfPos struct {
	Action
	keyName string
}

func (this *SetBoardSelfPos) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.keyName = setting.GetPropertyAsString("keyName")
}

func (this *SetBoardSelfPos) OnTick(tick *Tick) b3.Status {
	//self, ok := tick.Blackboard.Get(SELF_OBJECT, tick.GetTree().GetID(), "").(*MapObject)
	//if !ok || self == nil {
	//	fmt.Println("MoveToObject error")
	//	return b3.FAILURE
	//}
	self := tick.GetTarget().(*MapObject)
	fmt.Println("SetBoardSelfPos ", self.name, self.x, self.y)
	tick.Blackboard.SetTree(this.keyName, []int{self.x, self.y}, tick.GetTree().GetID())
	return b3.SUCCESS
}

type SetBoardTargetPos struct {
	Action
	keyName string
	target string
}

func (this *SetBoardTargetPos) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.keyName = setting.GetPropertyAsString("keyName")
	this.target = setting.GetPropertyAsString("target")
}

func (this *SetBoardTargetPos) OnTick(tick *Tick) b3.Status {
	target, ok := tick.Blackboard.Get(this.target, tick.GetTree().GetID(),"").(*MapObject)
	if !ok || target == nil{
		fmt.Println("SetBoardTargetPos error")
		return b3.FAILURE
	}

	fmt.Println("SetBoardTargetPos ", target.name, target.x, target.y)
	tick.Blackboard.SetTree(this.keyName, []int{target.x, target.y}, tick.GetTree().GetID())
	return b3.SUCCESS
}

type SetBoardConst struct {
	Action
	keyName string
	value int
}

func (this *SetBoardConst) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.keyName = setting.GetPropertyAsString("keyName")
	this.value = setting.GetPropertyAsInt("value")
}

func (this *SetBoardConst) OnTick(tick *Tick) b3.Status {
	fmt.Println("SetBoardConst ", this.keyName, this.value)
	tick.Blackboard.SetTree(this.keyName, this.value, tick.GetTree().GetID())
	return b3.SUCCESS
}


type MoveToObject struct {
	Action
	targetKey string
}

func (this *MoveToObject) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.targetKey = setting.GetPropertyAsString("target")
}

func (this *MoveToObject) OnTick(tick *Tick) b3.Status {
	//self, ok := tick.Blackboard.Get(SELF_OBJECT, tick.GetTree().GetID(), "").(*MapObject)
	//if !ok || self == nil {
	//	fmt.Println("MoveToObject error")
	//	return b3.FAILURE
	//}

	target, ok := tick.Blackboard.Get(this.targetKey, tick.GetTree().GetID(), "").(*MapObject)
	if !ok || target == nil {
		fmt.Println("MoveToObject error")
		return b3.FAILURE
	}
	self := tick.GetTarget().(*MapObject)
	if self.y != target.y {
		if self.y > target.y {
			self.y -= 1
		} else {
			self.y += 1
		}

		fmt.Println("MoveToObject moving", target, self.x, self.y)
		return b3.RUNNING
	}

	fmt.Println("MoveToObject move end")
	return b3.SUCCESS
}

type MoveToBoard struct {
	Action
	keyName string
}

func (this *MoveToBoard) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.keyName = setting.GetPropertyAsString("keyName")
}

func (this *MoveToBoard) OnTick(tick *Tick) b3.Status {
	//self, ok := tick.Blackboard.Get(SELF_OBJECT, tick.GetTree().GetID(), "").(*MapObject)
	//if !ok || self == nil {
	//	fmt.Println("MoveToBoard error")
	//	return b3.FAILURE
	//}
	self := tick.GetTarget().(*MapObject)
	targetPos, ok := tick.Blackboard.Get(this.keyName, tick.GetTree().GetID(), "").([]int)
	if !ok || len(targetPos) != 2 {
		fmt.Println("MoveToBoard error")
		return b3.FAILURE
	}


	if self.y != targetPos[1] {
		if self.y > targetPos[1] {
			self.y -= 1
		} else {
			self.y += 1
		}

		fmt.Println("MoveToBoard moving ", self.x, self.y, targetPos)
		return b3.RUNNING
	}

	fmt.Println("MoveToBoard move end ", self.x, self.y, targetPos)
	return b3.SUCCESS
}

type SelectSkill struct {
	Action
	skillKey string
}

func (this *SelectSkill) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.skillKey = setting.GetPropertyAsString("skillKey")
}

func (this *SelectSkill) OnTick(tick *Tick) b3.Status {
	fmt.Println("SelectSkill 1")
	tick.Blackboard.SetTree(this.skillKey, 1, tick.GetTree().GetID())
	return b3.SUCCESS
}

type Attack struct {
	Action
	skillKey string
	targetKey string
}

func (this *Attack) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
	this.skillKey = setting.GetPropertyAsString("skillKey")
	this.targetKey = setting.GetPropertyAsString("targetKey")
}

func (this *Attack) OnTick(tick *Tick) b3.Status {
	target, ok := tick.Blackboard.Get(this.targetKey, tick.GetTree().GetID(), "").(*MapObject)
	if !ok || target == nil{
		fmt.Println("Attack target error")
		return b3.FAILURE
	}

	target.hp -= 30

	if target.hp <= 0 {
		fmt.Println("Attack end")
		return b3.FAILURE
	}
	fmt.Printf("Attack target[%s], hp -30, hp:%d\n", target.name, target.hp)
	//tick.Blackboard.SetTree(this.skillKey, 1, tick.GetTree().GetID())
	return b3.SUCCESS
}

type Idle struct {
	Action
}

func (this *Idle) Initialize(setting *BTNodeCfg) {
	this.Action.Initialize(setting)
}

func (this *Idle) OnTick(tick *Tick) b3.Status {
	fmt.Println("Idle")
	return b3.SUCCESS
}

type BoardEqualSelfPos struct {
	Condition
	posKey string
}

func (this *BoardEqualSelfPos) Initialize(setting *BTNodeCfg) {
	this.Condition.Initialize(setting)
	this.posKey = setting.GetPropertyAsString("posKey")
}

func (this *BoardEqualSelfPos) OnTick(tick *Tick) b3.Status {
	self := tick.GetTarget().(*MapObject)
	checkPos, ok := tick.Blackboard.Get(this.posKey, tick.GetTree().GetID(), "").([]int)
	if !ok {
		fmt.Println("BoardEqualSelfPos error")
		return b3.FAILURE
	}

	if len(checkPos) != 2 {
		fmt.Println("BoardEqualSelfPos pos error")
		return b3.FAILURE
	}

	if checkPos[1] == self.y {
		fmt.Println("BoardEqualSelfPos true")
		return b3.SUCCESS
	}
	fmt.Println("BoardEqualSelfPos false")
	return b3.FAILURE
}

type InRange struct {
	Condition
	centerPos string
	checkPos string
	dis int
}


func (this *InRange) Initialize(setting *BTNodeCfg) {
	this.Condition.Initialize(setting)
	this.centerPos = setting.GetPropertyAsString("CenterPos")
	this.checkPos = setting.GetPropertyAsString("CheckPos")
	this.dis = setting.GetPropertyAsInt("Dis")
}

func (this *InRange) OnTick(tick *Tick) b3.Status {
	checkPos, ok := tick.Blackboard.Get(this.checkPos, tick.GetTree().GetID(), "").([]int)
	if !ok {
		fmt.Println("InRange error")
		return b3.FAILURE
	}

	if len(checkPos) != 2 {
		fmt.Println("InRange pos error")
		return b3.FAILURE
	}

	selfPos, ok := tick.Blackboard.Get(this.centerPos, tick.GetTree().GetID(), "").([]int)
	if !ok {
		fmt.Println("InRange error")
		return b3.FAILURE
	}

	if len(selfPos) != 2 {
		fmt.Println("InRange pos error")
		return b3.FAILURE
	}

	if checkPos[1] - selfPos[1] > this.dis {
		fmt.Println("InRange false")
		return b3.FAILURE
	}
	fmt.Println("InRange true")
	return b3.SUCCESS
}


type BoardEqualConst struct {
	Condition
	keyName string
	value int
}

func (this *BoardEqualConst) Initialize(setting *BTNodeCfg) {
	this.Condition.Initialize(setting)
	this.keyName = setting.GetPropertyAsString("keyName")
	this.value = setting.GetPropertyAsInt("value")
}

func (this *BoardEqualConst) OnTick(tick *Tick) b3.Status {
	v := tick.Blackboard.GetInt(this.keyName, tick.GetTree().GetID(), "")
	result := v == this.value
	fmt.Println("BoardEqualConst ", this.keyName, this.value, result)
	if result {
		return b3.SUCCESS
	}
	return b3.FAILURE
}


type BoardConstCompare struct {
	Decorator
	keyName string
	operator string
	value int
}

func (this *BoardConstCompare) Initialize(setting *BTNodeCfg) {
	this.Decorator.Initialize(setting)
	this.keyName = setting.GetPropertyAsString("keyName")
	this.value = setting.GetPropertyAsInt("value")
	this.operator = setting.GetPropertyAsString("operator")
	if this.operator != "=" &&  this.operator != "!=" &&this.operator != "<" &&this.operator != ">" {
		panic("param \"operator\" invalid")
	}
}

func (this *BoardConstCompare) OnTick(tick *Tick) b3.Status {
	v := tick.Blackboard.GetInt(this.keyName, tick.GetTree().GetID(), "")
	var result bool
	switch this.operator {
	case "=":
		result = v == this.value
	case "<":
		result = v < this.value
	case ">":
		result = v > this.value
	case "!=":
		result = v != this.value
	}

	fmt.Println("Decorator ", this.keyName, this.value, result)
	if result {
		return this.GetChild().Execute(tick)
	}
	return b3.FAILURE
}



func testBehavior() {
	projectConfig, ok := LoadProjectCfg("ai.b3")
	if !ok {
		fmt.Println("LoadTreeCfg err")
		return
	}

	//自定义节点注册
	maps := b3.NewRegisterStructMaps()
	maps.Register("BoardEqualConst", new(BoardEqualConst))
	maps.Register("InRange", new(InRange))
	maps.Register("BoardEqualSelfPos", new(BoardEqualSelfPos))
	maps.Register("SetBoardConst", new(SetBoardConst))
	maps.Register("MoveToObject", new(MoveToObject))
	maps.Register("SelectSkill", new(SelectSkill))
	maps.Register("MoveToBoard", new(MoveToBoard))
	maps.Register("SearchTarget", new(SearchTargetList))
	maps.Register("SelectTarget", new(SelectTarget))
	maps.Register("SetBoardSelfPos", new(SetBoardSelfPos))
	maps.Register("SetBoardTargetPos", new(SetBoardTargetPos))
	maps.Register("Idle", new(Idle))
	maps.Register("Attack", new(Attack))
	maps.Register("BoardConstCompare", new(BoardConstCompare))

	trees := make(map[string]*BehaviorTree, 3)
	var mainTree *BehaviorTree
	//载入
	for _, v := range projectConfig.Trees {
		tree := CreateBevTreeFromConfig(&v, maps)
		tree.Print()
		trees[v.ID] = tree

		if tree.GetTitile() == "main_state" {
			mainTree = tree
		}
	}

	SetSubTreeLoadFunc(func(s string) *BehaviorTree {
		return trees[s]
	})

	//输入板
	board := NewBlackboard()
	world :=  make(map[string]*MapObject)
	self := &MapObject{
		name: "self",
		x:    0,
		y:    0,
		hp:   100,
		world: world,
	}

	m1 := &MapObject {
		name: "player1",
		x : 0,
		y : 5,
		hp : 100,
		world: world,
	}
	m2 := &MapObject {
		name: "player2",
		x : 0,
		y : 7,
		hp : 200,
		world: world,
	}


	world[self.name] = self
	world[m1.name] = m1
	world[m2.name] = m2

	for i := 0; i < 30; i++ {
		time.Sleep( time.Second)
		mainTree.Tick(self, board)
		fmt.Println("-----------------------------------")
	}
}
